/*
	bootmod.cc

	Boot Modules Initialization

	- Support For GRUB Modules
*/
#include <stdint.h>
#include "kernel.h"
#include "debug.h"
#include "bootmod.h"
#include "pm.h"
#include "vm.h"
#include "elf.h"
#include "heap.h"
#include "mt.h"

#define ET_EXEC 2
#define EM_386 3

#define ROUNDUP(x,y) (x / y + (x % y > 0 ? 1 : 0))
int bootmod_grub(unsigned long modaddr, unsigned long modcount)
{
	struct tbootmod_grub_mod *gm = (struct tbootmod_grub_mod*)modaddr;
	elf32_ehdr *ehdr;
	elf32_phdr *phdr;
	uint8_t *sd;
	uint32_t x, y, z, *vmd;
	sched_threadDescription td;

	/// map the module array
	vm_map(g_k_vmd, g_k_vmd, modaddr, modaddr, VM_KERNEL);

	for(x = 0; x < modcount; ++x)
	{
		//kdebugf("bootmod_grub: %x-%x, string:%s, reserved:%x\n", gm[x].start, gm[x].end, gm[x].string, gm[x].reserved);
		/// map the module memory
		vm_map2(g_k_vmd, g_k_vmd, gm[x].start, gm[x].start, VM_KERNEL, (gm[x].end - gm[x].start) / 4096 + ((gm[x].end - gm[x].start) % 4096 > 0 ? 1 : 0));
		/// look through ELF header for the .text, .data, .rodata, and .bss sections.
		ehdr = (elf32_ehdr*)gm[x].start;
		if(ehdr->e_ident[0] != 0x7f)
		{
			kdbginfo("boot module %s did not have the correct ELF32 signature; skipping.\n", gm[x].string);
			continue;
		}
		if(ehdr->e_type != ET_EXEC)
		{
			kdbginfo("boot module %s was not a ET_EXEC type; skipping.\n", gm[x].string);
			continue;
		}
		if(ehdr->e_machine != EM_386)
		{
			kdbginfo("boot module %s target machine was not 386; skipping.\n", gm[x].string);
			continue;
		}
		kdbginfo("creating thread descriptor.\n");
		phdr = (elf32_phdr*)(ehdr->e_phoff + gm[x].start);
		/// create a new thread descriptor and address space.
		memset(&td, 0, sizeof(td));
		vmd = (uint32_t*)vm_look_what(g_k_vmd, vm_make(g_k_vmd));
		td.contextId = (uint32_t)vmd;
		td.processId = 0;	
		td.entry = ehdr->e_entry;
		td.priority = 0;
		td.kstackSize = 1;
		td.ustackSize = 4;
		td.cs = SCHED_USER_CODEDESC;
		td.registers.ds = SCHED_USER_DATADESC;
		td.registers.es = SCHED_USER_DATADESC;
		td.registers.fs = SCHED_USER_DATADESC;
		td.registers.gs = SCHED_USER_DATADESC;
		td.registers.ss = SCHED_USER_DATADESC;
		td.eflags = SCHED_EFLAGS32;
		td.medaddr = 0;

		kdbginfo("e_entry:%x\n", ehdr->e_entry);

		/// map kernel into address space (for interrupt entrance)
		vm_map2(g_k_vmd, vmd, (uintptr_t)&__start, (uintptr_t)&__start, VM_KERNEL,
					 ( ( (uintptr_t) &__end - (uintptr_t) &__start ) / 4096 ) + 1);
		/// display the program headers, and load them into their new address space.
		for(y = 0; y < ehdr->e_phnum; ++y)
		{
			if(phdr->ph_memsz == 0)
			{
				continue;
			}
			kdbginfo("   segment(%x)- vaddr:%x paddr:%x\n", y, phdr[y].ph_vaddr, phdr[y].ph_paddr);
			/// allocate enough space to hold the program segment.
			sd = (uint8_t*)vm_alloc2(g_k_vmd, g_k_vmd, ROUNDUP(phdr[y].ph_memsz, 0x1000), VM_KERNEL);
			for(z = 0; z < phdr[y].ph_filesz; ++z)
			{
				/// copy segment into new location aligned on page boundary.
				sd[z + (phdr[y].ph_vaddr & 0xFFF)] = ((uint8_t*)(phdr[y].ph_offset + gm[x].start))[z];
				//kdebugf("byte:%x laddr:%x faddr:%x vaddr:%x\n", ((uint8_t*)(phdr[y].ph_offset + gm[x].start))[z],
				//			z + (phdr[y].ph_vaddr & 0xFFF),
				//			phdr[y].ph_offset + gm[x].start + z,
				//			phdr[y].ph_vaddr + z
				//		);
			}	
			kdbginfo("copying\n");
			/// copy current mapping to new address space, and free the memory we allocated to do it.
			vm_copy2(g_k_vmd, g_k_vmd, vmd, (uint32_t)sd, phdr[y].ph_vaddr, ROUNDUP(phdr[y].ph_memsz, 0x1000), VM_USER);
			/// free our current copy, which will leave only where we copied it above.
			//vm_free2(g_k_vmd, g_k_vmd, (uint32_t)sd, ROUNDUP(phdr[y].ph_memsz, 0x1000));
		}
		vm_map(g_k_vmd, vmd, (uint32_t)g_k_gdt, (uint32_t)g_k_gdt, VM_KERNEL);

		/// start thread.
		sched_create_thread(&td, 0);

		/// free the memory reserved during the early stages of booting.
		vm_free2(g_k_vmd, g_k_vmd, gm[x].start, (gm[x].end - gm[x].start) / 4096 + ((gm[x].end - gm[x].start) % 4096 > 0 ? 1 : 0));
	}
	/// free the memory reserved for modaddr.
	vm_free(g_k_vmd, g_k_vmd, modaddr);
	return 1;
}

